﻿using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using KaytechSoftware.Cms.Services.Common;
using KaytechSoftware.Cms.Common.Helpers;
using KaytechSoftware.Cms.Services.Interfaces.Miscellaneous;

namespace KaytechSoftware.Cms.Services.Services.Miscellaneous
{
    public class ImageService : IImageService
    {
        private const int LongSideThumbSize = 92;
        private const int ShortSideThumbSize = 68;

        private const int LongSideSmallSize = 2 * LongSideThumbSize;
        private const int ShortSideSmallSize = 2 * ShortSideThumbSize;

        private const int LongSideMaximumPhotoSize = 720;
        private const int ShortSideMaximumPhotoSize = 540;
        private const int LongSideMinimumPhotoSize = 360;
        private const int ShortSideMinimumPhotoSize = 270;

        public void UploadPhoto(long photoAlbumId, long photoId, byte[] data)
        {
            var image = Image.FromStream(new MemoryStream(data));
            var scaledImage = ScaleImage(image, FindProperSizeForImage(image), false);
            var scaledSmallImage = ScaleImage(image, FindProperSizeForSmallImage(image), true);
            var scaledImageThumb = ScaleImage(image, FindProperSizeForThumb(image), true);

            var photoPath = PathHelper.PhotoPathForServer(photoAlbumId, photoId);
            var smallPhotoPath = PathHelper.PhotoSmallPathForServer(photoAlbumId, photoId);
            var photoThumbPath = PathHelper.PhotoThumbPathForServer(photoAlbumId, photoId);

            PathHelper.AssurePhysicalPathExists(photoPath);
            PathHelper.AssurePhysicalPathExists(smallPhotoPath);
            PathHelper.AssurePhysicalPathExists(photoThumbPath);

            scaledImage.Save(photoPath);
            scaledSmallImage.Save(smallPhotoPath);
            scaledImageThumb.Save(photoThumbPath);
        }

        private ImageSize FindProperSizeForThumb(Image image)
        {
            if (image.Width >= image.Height)
            {
                return new ImageSize(LongSideThumbSize, ShortSideThumbSize);
            }

            return new ImageSize(ShortSideThumbSize, LongSideThumbSize);
        }

        private ImageSize FindProperSizeForSmallImage(Image image)
        {
            if (image.Width >= image.Height)
            {
                return new ImageSize(LongSideSmallSize, ShortSideSmallSize);
            }

            return new ImageSize(ShortSideThumbSize, LongSideThumbSize);
        }

        private ImageSize FindProperSizeForImage(Image image)
        {
            var isHorizontal = image.Width >= image.Height;
            var longSide = isHorizontal ? image.Width : image.Height;
            var shortSide = isHorizontal ? image.Width : image.Height;

            if (longSide > LongSideMaximumPhotoSize || shortSide > ShortSideMaximumPhotoSize)
            {
                return isHorizontal ? new ImageSize(LongSideMaximumPhotoSize, ShortSideMaximumPhotoSize) : new ImageSize(ShortSideMaximumPhotoSize, LongSideMaximumPhotoSize); 
            }

            if (longSide < LongSideMinimumPhotoSize || shortSide < ShortSideMinimumPhotoSize)
            {
                return isHorizontal ? new ImageSize(LongSideMinimumPhotoSize, ShortSideMinimumPhotoSize) : new ImageSize(ShortSideMinimumPhotoSize, LongSideMinimumPhotoSize); 
            }

            return new ImageSize(image.Width, image.Height);
        }

        private Bitmap ScaleImage(Image image, ImageSize requestedSize, bool cropImage)
        {
            var targetSize = DetermineProportionalSize(new ImageSize { Width = image.Width, Height = image.Height }, requestedSize, cropImage);
            var bitmap = new Bitmap(targetSize.Width, targetSize.Height, PixelFormat.Format32bppPArgb);
            bitmap.SetResolution(image.HorizontalResolution, image.VerticalResolution);
            bitmap.MakeTransparent(Color.FromArgb(255, 255, 255));
            using (var graphics = Graphics.FromImage(bitmap))
            {
                graphics.Clear(Color.FromArgb(255, 255, 255));
                graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
                graphics.CompositingQuality = CompositingQuality.HighQuality;
                graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
                graphics.SmoothingMode = SmoothingMode.HighQuality;
                graphics.DrawImage(image, new Rectangle(0, 0, targetSize.Width, targetSize.Height), new Rectangle(0, 0, image.Width, image.Height), GraphicsUnit.Pixel);
            }

            if (cropImage)
                bitmap = CropImage(bitmap, requestedSize);
            
            return bitmap;
        }

        private ImageSize DetermineProportionalSize(ImageSize sourceImageSize, ImageSize requestedSize, bool cropImage)
        {
            var widthRatio = (double)requestedSize.Width / (double)sourceImageSize.Width;
            var heightRatio = (double)requestedSize.Height / (double)sourceImageSize.Height;

            int width;
            int height;
            if (cropImage)
            {
                width = (int)Math.Round(Math.Max(requestedSize.Width, Math.Max(widthRatio, heightRatio) * sourceImageSize.Width));
                height = (int)Math.Round(Math.Max(requestedSize.Height, Math.Max(widthRatio, heightRatio) * sourceImageSize.Height));
            }
            else
            {
                width = (int)Math.Round(Math.Min(requestedSize.Width, Math.Min(widthRatio, heightRatio) * sourceImageSize.Width));
                height = (int)Math.Round(Math.Min(requestedSize.Height, Math.Min(widthRatio, heightRatio) * sourceImageSize.Height));
            }

            return new ImageSize
                {
                    Width = width,
                    Height = height
                };
        }

        private Bitmap CropImage(Image image, ImageSize requestedSize)
        {
            var dx = Math.Abs(image.Width - requestedSize.Width) / 2;
            var dy = Math.Abs(image.Height - requestedSize.Height) / 2;
            var frame = new Rectangle(dx, dy, requestedSize.Width, requestedSize.Height);
            var bitmap = new Bitmap(requestedSize.Width, requestedSize.Height, PixelFormat.Format32bppPArgb);

            using (Graphics graphics = Graphics.FromImage(bitmap))
            {
                graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
                graphics.CompositingQuality = CompositingQuality.HighQuality;
                graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
                graphics.SmoothingMode = SmoothingMode.HighQuality;
                graphics.DrawImage(image, new Rectangle(0, 0, requestedSize.Width, requestedSize.Height), frame, GraphicsUnit.Pixel);
            }

            return bitmap;
        }
    }
}
